"""
网络通信模块
负责UDP音频数据传输、房间管理、成员发现
"""
import socket
import threading
import json
import time
from typing import Callable, Dict, List, Set


class Member:
    """房间成员"""
    def __init__(self, name: str, ip: str, last_seen: float = None):
        self.name = name
        self.ip = ip
        self.last_seen = last_seen or time.time()
    
    def to_dict(self):
        return {"name": self.name, "ip": self.ip}
    
    @staticmethod
    def from_dict(data):
        return Member(data["name"], data["ip"])


class NetworkManager:
    """网络管理器"""
    
    # 消息类型
    MSG_AUDIO = "AUDIO"
    MSG_JOIN = "JOIN"
    MSG_LEAVE = "LEAVE"
    MSG_HEARTBEAT = "HEARTBEAT"
    MSG_MEMBERS = "MEMBERS"
    MSG_MEMBER_LIST = "MEMBER_LIST"
    MSG_ROOM_RESTART = "ROOM_RESTART"  # 房间重启（房主重连）
    
    def __init__(self, port: int = 9527, config_manager=None):
        self.port = port
        self.socket = None
        self.running = False
        self.receive_thread = None
        self.config_manager = config_manager  # 添加config_manager引用
        
        # 房间信息
        self.is_host = False
        self.room_name = ""
        self.user_name = ""
        self.members: Dict[str, Member] = {}  # ip -> Member
        
        # 回调函数
        self.on_audio_received: Callable[[str, bytes], None] = None
        self.on_member_joined: Callable[[str, str], None] = None
        self.on_member_left: Callable[[str], None] = None
        self.on_members_updated: Callable[[List[Member]], None] = None
        
        # 心跳线程
        self.heartbeat_thread = None
        self.last_heartbeat = {}
        
        # 日志计数器（减少日志输出）
        self.send_log_counter = 0
        self.receive_log_counter = 0
    
    @property
    def in_room(self):
        """是否在房间中"""
        return bool(self.room_name and (self.is_host or self.members))
    
    def start(self, user_name: str):
        """启动网络服务"""
        self.user_name = user_name
        try:
            self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self.socket.bind(("0.0.0.0", self.port))
            self.running = True
            
            # 启动接收线程
            self.receive_thread = threading.Thread(target=self._receive_loop, daemon=True)
            self.receive_thread.start()
            
            # 启动心跳线程
            self.heartbeat_thread = threading.Thread(target=self._heartbeat_loop, daemon=True)
            self.heartbeat_thread.start()
            
            return True
        except Exception as e:
            print(f"启动网络服务失败: {e}")
            return False
    
    def stop(self):
        """停止网络服务"""
        self.running = False
        if self.is_host or self.members:
            self._broadcast_message(self.MSG_LEAVE, {"user": self.user_name})
        
        if self.socket:
            self.socket.close()
        self.members.clear()
    
    def create_room(self, room_name: str):
        """创建房间（作为房主）"""
        self.is_host = True
        self.room_name = room_name
        local_ip = self._get_local_ip()
        self.members[local_ip] = Member(self.user_name, local_ip)
    
    def join_room(self, room_name: str, host_ip: str):
        """加入房间"""
        self.is_host = False
        self.room_name = room_name
        self.members[host_ip] = Member("房主", host_ip)
        
        # 发送加入请求
        self._send_message(host_ip, self.MSG_JOIN, {
            "user": self.user_name,
            "room": room_name,
            "ip": self._get_local_ip()
        })
    
    def leave_room(self):
        """离开房间"""
        self._broadcast_message(self.MSG_LEAVE, {"user": self.user_name})
        self.members.clear()
        self.room_name = ""
        self.is_host = False
    
    def notify_room_restart(self, member_ips: list, retry_count: int = 3):
        """房主重连时通知之前的成员
        
        Args:
            member_ips: 之前保存的成员IP列表
            retry_count: 重试次数，默认3次
        """
        if not self.is_host:
            return
        
        print(f"[NetworkManager] 房主重连，通知 {len(member_ips)} 个成员（重试{retry_count}次）")
        
        local_ip = self._get_local_ip()
        
        # 向每个之前的成员发送房间重启消息
        for ip in member_ips:
            if ip == local_ip:  # 跳过自己
                continue
            
            # 多次尝试发送，提高成功率
            success = False
            for attempt in range(retry_count):
                try:
                    self._send_message(ip, self.MSG_ROOM_RESTART, {
                        "room": self.room_name,
                        "host": self.user_name,
                        "host_ip": local_ip
                    })
                    
                    if attempt == 0:
                        print(f"[NetworkManager] 已通知成员: {ip}")
                    else:
                        print(f"[NetworkManager] 重试 {attempt+1} 次通知成员: {ip}")
                    
                    success = True
                    break  # 成功则退出重试循环
                    
                except Exception as e:
                    if attempt == retry_count - 1:  # 最后一次尝试失败
                        print(f"[NetworkManager] 通知成员 {ip} 失败（已重试{retry_count}次）: {e}")
                    else:
                        # 短暂延迟后重试
                        import time
                        time.sleep(0.05)  # 50ms延迟
            
            if success:
                print(f"[NetworkManager] ✓ 成员 {ip} 已收到通知")
            else:
                print(f"[NetworkManager] ✗ 成员 {ip} 可能已离线")
    
    def send_audio(self, audio_data: bytes):
        """发送音频数据（优化版：减少日志输出）"""
        if not self.members:
            # print(f"[NetworkManager] 无法发送：没有成员")
            return
        
        # 减少日志输出（每20次输出一次）
        self.send_log_counter += 1
        if self.send_log_counter % 20 == 0:
            print(f"[NetworkManager] 发送音频 #{self.send_log_counter}: {len(audio_data)} 字节 -> {len(self.members)} 成员")
        
        # 直接发送（UDP最大约65KB，我们限制在60KB）
        if len(audio_data) <= 60000:
            try:
                self._broadcast_message(self.MSG_AUDIO, {
                    "user": self.user_name,
                    "data": audio_data.hex()
                })
            except Exception as e:
                # 减少错误日志频率
                if self.send_log_counter % 50 == 0:
                    print(f"[NetworkManager] 发送音频失败: {e}")
        else:
            # 数据太大，分块发送
            chunk_size = 30000  # 30KB per chunk
            chunks = [audio_data[i:i+chunk_size] for i in range(0, len(audio_data), chunk_size)]
            
            for i, chunk in enumerate(chunks):
                try:
                    self._broadcast_message(self.MSG_AUDIO, {
                        "user": self.user_name,
                        "data": chunk.hex(),
                        "chunk": i,
                        "total": len(chunks)
                    })
                except Exception as e:
                    # 减少错误日志频率
                    if i == 0:  # 只在第一块失败时输出
                        print(f"[NetworkManager] 发送音频块{i}失败: {e}")
    
    def _send_message(self, ip: str, msg_type: str, data: dict):
        """发送消息到指定IP"""
        try:
            message = {
                "type": msg_type,
                "data": data
            }
            json_data = json.dumps(message).encode('utf-8')
            self.socket.sendto(json_data, (ip, self.port))
        except Exception as e:
            print(f"发送消息失败 ({ip}): {e}")
    
    def _broadcast_message(self, msg_type: str, data: dict):
        """广播消息到所有成员"""
        local_ip = self._get_local_ip()
        for ip in list(self.members.keys()):
            if ip != local_ip:  # 不发送给自己
                self._send_message(ip, msg_type, data)
    
    def _receive_loop(self):
        """接收消息循环"""
        while self.running:
            try:
                data, addr = self.socket.recvfrom(65535)
                self._handle_message(data, addr[0])
            except Exception as e:
                if self.running:
                    print(f"接收消息错误: {e}")
    
    def _handle_message(self, data: bytes, sender_ip: str):
        """处理接收到的消息"""
        try:
            message = json.loads(data.decode('utf-8'))
            msg_type = message.get("type")
            msg_data = message.get("data", {})
            
            if msg_type == self.MSG_AUDIO:
                # 音频数据（优化：减少日志输出）
                audio_hex = msg_data.get("data", "")
                user = msg_data.get("user", "未知")
                
                # 减少日志输出（每30次输出一次）
                self.receive_log_counter += 1
                # if self.receive_log_counter % 30 == 0:
                #     print(f"[NetworkManager] 接收音频 #{self.receive_log_counter} from {user}, {len(audio_hex)//2} 字节")
                
                try:
                    audio_data = bytes.fromhex(audio_hex)
                    
                    if self.on_audio_received:
                        # 快速传递给回调，不要阻塞接收线程
                        self.on_audio_received(user, audio_data)
                    # else:
                    #     print(f"[NetworkManager] 警告：on_audio_received回调未设置")
                except Exception as e:
                    # 减少错误日志频率
                    if self.receive_log_counter % 100 == 0:
                        print(f"[NetworkManager] 处理音频数据失败: {e}")
            
            elif msg_type == self.MSG_JOIN:
                # 有人加入房间
                user = msg_data.get("user")
                user_ip = msg_data.get("ip", sender_ip)
                room = msg_data.get("room", "")
                
                print(f"[NetworkManager] ========== 收到加入请求 ==========")
                print(f"[NetworkManager] 用户: {user} ({user_ip})")
                print(f"[NetworkManager] 房间: {room}")
                
                if self.is_host:
                    # 房主：添加成员并广播成员列表
                    print(f"[NetworkManager] 房主确认：添加成员 {user}")
                    self.members[user_ip] = Member(user, user_ip)
                    
                    if self.on_member_joined:
                        self.on_member_joined(user, user_ip)
                    
                    # 发送当前成员列表给所有人
                    member_list = [m.to_dict() for m in self.members.values()]
                    print(f"[NetworkManager] 广播成员列表（共 {len(member_list)} 人）")
                    self._broadcast_message(self.MSG_MEMBER_LIST, {"members": member_list})
                    print(f"[NetworkManager] ✓ 成员 {user} 已成功加入房间")
                else:
                    print(f"[NetworkManager] 忽略（本机不是房主）")
                print(f"[NetworkManager] ============================================")
            
            elif msg_type == self.MSG_LEAVE:
                # 有人离开房间
                user = msg_data.get("user")
                if sender_ip in self.members:
                    del self.members[sender_ip]
                    if self.on_member_left:
                        self.on_member_left(user)
                    
                    if self.is_host:
                        member_list = [m.to_dict() for m in self.members.values()]
                        self._broadcast_message(self.MSG_MEMBER_LIST, {"members": member_list})
            
            elif msg_type == self.MSG_ROOM_RESTART:
                # 房主重连，房间重启
                room_name = msg_data.get("room")
                host_name = msg_data.get("host")
                host_ip = msg_data.get("host_ip")
                
                print(f"[NetworkManager] ========== 收到房间重启通知 ==========")
                print(f"[NetworkManager] 房间: {room_name}")
                print(f"[NetworkManager] 房主: {host_name} ({host_ip})")
                
                # 如果当前不是房主，自动重新加入房间
                if not self.is_host:
                    self.room_name = room_name
                    self.members.clear()
                    self.members[host_ip] = Member(host_name, host_ip)
                    
                    print(f"[NetworkManager] 准备重新加入房间...")
                    
                    # 发送加入请求（关键：让房主知道我还在线）
                    self._send_message(host_ip, self.MSG_JOIN, {
                        "user": self.user_name,
                        "room": room_name,
                        "ip": self._get_local_ip()
                    })
                    
                    print(f"[NetworkManager] ✓ 已发送加入请求到房主 {host_ip}")
                    print(f"[NetworkManager] 等待房主确认...")
                else:
                    print(f"[NetworkManager] 忽略（本机就是房主）")
                print(f"[NetworkManager] ============================================")
            
            elif msg_type == self.MSG_MEMBER_LIST:
                # 收到成员列表更新
                members_data = msg_data.get("members", [])
                self.members.clear()
                for m_data in members_data:
                    member = Member.from_dict(m_data)
                    self.members[member.ip] = member
                
                if self.on_members_updated:
                    self.on_members_updated(list(self.members.values()))
            
            elif msg_type == self.MSG_HEARTBEAT:
                # 心跳包
                self.last_heartbeat[sender_ip] = time.time()
        
        except Exception as e:
            print(f"处理消息失败: {e}")
    
    def _heartbeat_loop(self):
        """心跳循环"""
        while self.running:
            time.sleep(5)
            # 发送心跳
            self._broadcast_message(self.MSG_HEARTBEAT, {"user": self.user_name})
            
            # 检查超时成员（30秒未响应）
            now = time.time()
            timeout_ips = []
            for ip, last_time in self.last_heartbeat.items():
                if now - last_time > 30:
                    timeout_ips.append(ip)
            
            for ip in timeout_ips:
                if ip in self.members:
                    member = self.members[ip]
                    del self.members[ip]
                    if self.on_member_left:
                        self.on_member_left(member.name)
                del self.last_heartbeat[ip]
    
    def _get_local_ip(self):
        """获取本机IP（使用用户在设置中选择的IP）"""
        # 优先使用config_manager中的IP（用户在设置中选择的）
        if self.config_manager:
            user_ip = self.config_manager.get_local_ip()
            if user_ip and user_ip != "127.0.0.1":
                print(f"[NetworkManager] 使用用户选择的IP: {user_ip}")
                return user_ip
        
        # 如果没有config_manager或没有选择IP，则自动获取
        try:
            # 获取所有网络接口的IP地址
            hostname = socket.gethostname()
            ip_list = socket.gethostbyname_ex(hostname)[2]
            
            # 过滤出局域网IP
            lan_ips = []
            for ip in ip_list:
                if ip.startswith('192.168.') or ip.startswith('10.') or ip.startswith('172.'):
                    lan_ips.append(ip)
            
            # 优先返回192.168开头的IP
            for ip in lan_ips:
                if ip.startswith('192.168.'):
                    return ip
            
            # 其次返回10.开头的IP
            for ip in lan_ips:
                if ip.startswith('10.'):
                    return ip
            
            # 返回172.开头的IP
            if lan_ips:
                return lan_ips[0]
            
            # 如果没找到局域网IP，使用备用方法
            s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            s.connect(("8.8.8.8", 80))
            ip = s.getsockname()[0]
            s.close()
            return ip
        except:
            return "127.0.0.1"
    
    def get_members_list(self) -> List[Member]:
        """获取成员列表"""
        return list(self.members.values())

